package com.miao.sqlSession;

import com.miao.executor.Executor;
import com.miao.pojo.Configuration;
import com.miao.pojo.MappedStatement;

import java.lang.reflect.*;
import java.sql.SQLException;
import java.util.List;

/**
 * @author 喵瑞恒
 * @date 2023/09/13 21:35
 **/
public class DefaultSqlSession implements SqlSession {
    private Configuration configuration;

    private Executor executor;

    public DefaultSqlSession(Configuration configuration, Executor executor) {
        this.configuration = configuration;
        this.executor = executor;
    }

    public <E> List<E> selectList(String statementId, Object param) throws Exception {
        //将查询操作委派给底层的执行器
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
        List<E> list = executor.query(configuration, mappedStatement, param);
        return list;
    }

    public <T> T selectOne(String statementId, Object param) throws Exception {
        //去调用selectList()方法
        List<Object> list = this.selectList(statementId, param);
        if (list.size() == 1) {
            return (T) list.get(0);
        } else if (list.size() > 1) {
            throw new RuntimeException("返回结果过多！");
        } else {
            return null;
        }
    }

    public void close() {
        executor.close();
    }

    public <T> T getMapper(Class<?> mapperClass) {
        //使用jdk动态代理生成基于接口的代理对象
        Object proxy = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
            /**
             *
             * @param proxy the proxy instance that the method was invoked on
             *
             * @param method the {@code Method} instance corresponding to
             * the interface method invoked on the proxy instance.  The declaring
             * class of the {@code Method} object will be the interface that
             * the method was declared in, which may be a superinterface of the
             * proxy interface that the proxy class inherits the method through.
             *
             * @param args an array of objects containing the values of the
             * arguments passed in the method invocation on the proxy instance,
             * or {@code null} if interface method takes no arguments.
             * Arguments of primitive types are wrapped in instances of the
             * appropriate primitive wrapper class, such as
             * {@code java.lang.Integer} or {@code java.lang.Boolean}.
             *
             * @return
             * @throws Throwable
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //具体逻辑：执行底层的JDBC
                // 通过调用sqlSession里面的方法来完成方法调用
                // 参数的准备： statementId 和 param

                String methodName = method.getName();// findAll
                String className = method.getDeclaringClass().getName();  //com.miao.dao.IUserDao
                String statementId = className + "." + methodName;

                //方法调用，需要改造当前工程：sqlCommandType
                MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
                // select delete update insert
                String sqlCommandType = mappedStatement.getSqlCommandType();
                if (sqlCommandType.equals("select")) {
                    Type genericReturnType = method.getGenericReturnType();
                    //判断是否实现了泛型类型参数化，如果带泛型，则为查询多个，如果不带泛型，则查询单个
                    if (genericReturnType instanceof ParameterizedType) {
                        if (args != null) {
                            return selectList(statementId, args[0]);
                        }
                        return selectList(statementId, null);
                    }
                    return selectOne(statementId, args[0]);

                } else if (sqlCommandType.equals("update")) {

                } else if (sqlCommandType.equals("delete")) {

                } else if (sqlCommandType.equals("insert")) {

                }

                return null;
            }
        });
        return (T) proxy;
    }
}
